#region license
/*
 * Copyright (C) 2007 CchenSoft.com
 * 
 * This library is free software; you can redistribute it and/or modify it 
 * under the terms of the GNU Lesser General Public License 2.1 or later, as
 * published by the Free Software Foundation. See the included License.txt
 * or http://www.gnu.org/copyleft/lesser.html for details.
 * 
 * Author: Billy Zhang
 */
#endregion

using System.Xml;
using System.Collections;
using System;
using System.Text;
using System.Collections.Generic;

namespace CchenSoft.Workflow.Loader
{
    public class ResultDescriptor : AbstractDescriptor, IValidatable
    {
        //~ Instance fields ////////////////////////////////////////////////////////

        protected List<FunctionDescriptor> postFunctions = new List<FunctionDescriptor>();
        protected List<FunctionDescriptor> preFunctions = new List<FunctionDescriptor>();
        protected List<ValidatorDescriptor> validators = new List<ValidatorDescriptor>();
        protected string displayName;
        protected string dueDate;
        protected string oldStatus;
        protected string owner;
        protected string status;
        protected bool hasStep = false;
        protected int join;
        protected int split;
        protected int step = 0;

        //~ Constructors ///////////////////////////////////////////////////////////

        /**
         * @deprecated use {@link DescriptorFactory} instead
         */
        public ResultDescriptor()
        {
        }

        /**
         * @deprecated use {@link DescriptorFactory} instead
         */
        public ResultDescriptor(XmlElement result)
        {
            Init(result);
        }

        //~ Methods ////////////////////////////////////////////////////////////////

        public string DisplayName
        {
            get { return displayName; }
            set
            {
                if (Parent is ActionDescriptor)
                {
                    if (((ActionDescriptor)Parent).Name.Equals(value))
                    {
                        this.displayName = null; // if displayName==parentAction.displayName, reset displayName  

                        return;
                    }
                }

                this.displayName = value;
            }
        }

        public string DueDate
        {
            get { return dueDate; }
        }

        public int Join
        {
            get { return join; }
            set { this.join = value; }
        }

        public string OldStatus
        {
            get { return oldStatus; }
            set { this.oldStatus = value; }
        }


        public string Owner
        {
            get { return owner; }
            set { this.owner = value; }
        }

        public IList<FunctionDescriptor> PostFunctions
        {
            get { return postFunctions; }
        }

        public IList<FunctionDescriptor> PreFunctions
        {
            get { return preFunctions; }
        }

        public int Split
        {
            get { return split; }
            set { this.split = value; }
        }

        public string Status
        {
            get { return status; }
            set { this.status = value; }
        }

        public int Step
        {
            get { return step; }
            set {
                this.step = value;
                hasStep = true;
            }
        }

        public IList<ValidatorDescriptor> Validators
        {
            get { return validators; }
        }

        public virtual void Validate()
        {
            ValidationHelper.Validate((ICollection<IValidatable>)preFunctions);
            ValidationHelper.Validate((ICollection<IValidatable>)postFunctions);
            ValidationHelper.Validate((ICollection<IValidatable>)validators);

            //if it's not a split or a join or a finish, then we require a next step
            if ((split == 0) &&
                (join == 0) &&
                !(Parent is ActionDescriptor) &&
                ((ActionDescriptor)Parent).Finish)
            {
                StringBuilder error = new StringBuilder("Result ");

                if (Id > 0)
                {
                    error.Append("#").Append(Id);
                }

                error.Append(" is not a split or join, and has no ");

                if (!hasStep)
                {
                    throw new InvalidWorkflowDescriptorException(error.Append("next step").ToString());
                }

                if ((status == null) || (status.Length == 0))
                {
                    throw new InvalidWorkflowDescriptorException(error.Append("status").ToString());
                }
            }

            //taken out for now
            //if ((split != 0) && ((join != 0) || (oldStatus.length() > 0) || (step != 0) || (status.length() > 0) || (owner.length() != 0))) {
            //    throw new InvalidWorkflowDescriptorException("Result " + id + " has a split attribute, so should not any other attributes");
            //} else if ((join != 0) && ((split != 0) || (oldStatus.length() > 0) || (step != 0) || (status.length() > 0) || (owner.length() != 0))) {
            //    throw new InvalidWorkflowDescriptorException("Result has a join attribute, should thus not any other attributes");
            //} else if ((oldStatus.length() == 0) || (step == 0) || (status.length() == 0)) {
            //    throw new InvalidWorkflowDescriptorException("old-status, step, status and owner attributes are required if no split or join");
            //}
        }

        public override void WriteXML(XmlTextWriter writer, int indent)
        {
            XMLUtil.printIndent(writer, indent++);

            writer.WriteStartElement("unconditional-result");

            if (HasId())
            {
                writer.WriteAttributeString("id", Id.ToString());
            }

            if ((dueDate != null) && (dueDate.Length > 0))
            {
                writer.WriteAttributeString("due-date", dueDate);
            }

            writer.WriteAttributeString("old-status", oldStatus);

            if (join != 0)
            {
                writer.WriteAttributeString("join=", join.ToString());
            }
            else if (split != 0)
            {
                writer.WriteAttributeString("split", split.ToString());
            }
            else
            {
                writer.WriteAttributeString("status=", status);
                writer.WriteAttributeString("step=", step.ToString());

                if ((owner != null) && (owner.Length > 0))
                {
                    writer.WriteAttributeString("owner=", owner);
                }

                if ((displayName != null) && (displayName.Length > 0))
                {
                    writer.WriteAttributeString("display-name", displayName);
                }
            }

            if (preFunctions.Count > 0)
                printPreFunctions(writer, indent);

            if (postFunctions.Count > 0)
                printPostFunctions(writer, indent);

            XMLUtil.printIndent(writer, --indent);
            writer.WriteEndElement(); // ("</unconditional-result>");
        }

        protected virtual void Init(XmlElement result)
        {
            oldStatus = XMLUtil.GetAttributeValue(result, "old-status");
            status = XMLUtil.GetAttributeValue(result, "status");

            try
            {
                Id = (Convert.ToInt32(XMLUtil.GetAttributeValue(result, "id")));
            }
            catch (FormatException e)
            {
            }

            dueDate = XMLUtil.GetAttributeValue(result, "due-date");

            try
            {
                split = Convert.ToInt32(XMLUtil.GetAttributeValue(result, "split"));
            }
            catch (Exception ex)
            {
            }

            try
            {
                join = Convert.ToInt32(XMLUtil.GetAttributeValue(result, "join"));
            }
            catch (Exception ex)
            {
            }

            try
            {
                step = Convert.ToInt32(XMLUtil.GetAttributeValue(result, "step"));
                hasStep = true;
            }
            catch (Exception ex)
            {
            }

            owner = XMLUtil.GetAttributeValue(result, "owner");
            displayName = XMLUtil.GetAttributeValue(result, "display-name");

            // set up validators -- OPTIONAL
            XmlNode v = result.SelectSingleNode("validators");

            if (v != null)
            {
                XmlNodeList validators = v.SelectNodes("validator");

                for (int k = 0; k < validators.Count; k++)
                {
                    XmlElement validator = (XmlElement)validators[k];
                    ValidatorDescriptor validatorDescriptor = DescriptorFactory.GetFactory().CreateValidatorDescriptor(validator);
                    validatorDescriptor.Parent = this;
                    this.validators.Add(validatorDescriptor);
                }
            }

            // set up pre-functions -- OPTIONAL
            XmlNode pre = result.SelectSingleNode("pre-functions");

            if (pre != null)
            {
                XmlNodeList preFunctions = pre.SelectNodes("function");

                for (int k = 0; k < preFunctions.Count; k++)
                {
                    XmlElement preFunction = (XmlElement)preFunctions[k];
                    FunctionDescriptor functionDescriptor = DescriptorFactory.GetFactory().CreateFunctionDescriptor(preFunction);
                    functionDescriptor.Parent = this;
                    this.preFunctions.Add(functionDescriptor);
                }
            }

            // set up post-functions - OPTIONAL
            XmlNode post = result.SelectSingleNode("post-functions");

            if (post != null)
            {
                XmlNodeList postFunctions = post.SelectNodes("function");

                for (int k = 0; k < postFunctions.Count; k++)
                {
                    XmlElement postFunction = (XmlElement)postFunctions[k];
                    FunctionDescriptor functionDescriptor = DescriptorFactory.GetFactory().CreateFunctionDescriptor(postFunction);
                    functionDescriptor.Parent = this;
                    this.postFunctions.Add(functionDescriptor);
                }
            }
        }

        protected void printPostFunctions(XmlTextWriter writer, int indent)
        {
            if (postFunctions.Count > 0)
            {
                XMLUtil.printIndent(writer, indent++);
                writer.WriteStartElement("post-functions");

                IEnumerator iter = postFunctions.GetEnumerator();

                while (iter.MoveNext())
                {
                    FunctionDescriptor function = (FunctionDescriptor)iter.Current;
                    function.WriteXML(writer, indent);
                }

                XMLUtil.printIndent(writer, --indent);
                writer.WriteEndElement(); // ("</post-functions>");
            }
        }

        protected void printPreFunctions(XmlTextWriter writer, int indent)
        {
            if (preFunctions.Count > 0)
            {
                XMLUtil.printIndent(writer, indent++);
                writer.WriteStartElement("pre-functions");

                IEnumerator iter = preFunctions.GetEnumerator();

                while (iter.MoveNext())
                {
                    FunctionDescriptor function = (FunctionDescriptor)iter.Current;
                    function.WriteXML(writer, indent);
                }

                XMLUtil.printIndent(writer, --indent);
                writer.WriteEndElement(); // ("</pre-functions>");
            }
        }
    }
}
